package br.mikhas.lab.lang;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import br.mikhas.lab.lang.tables.ClassTable;
import br.mikhas.lab.lang.tables.StringTable;

public class Class {

	private static final Map<String, Integer> primitiveSizes;

	static {
		primitiveSizes = new HashMap<String, Integer>();
		primitiveSizes.put("boolean", 1);
		primitiveSizes.put("byte", 1);
		primitiveSizes.put("short", 2);
		primitiveSizes.put("integer", 4);
		primitiveSizes.put("long", 8);
		primitiveSizes.put("float", 4);
		primitiveSizes.put("double", 8);
	}

	private final StringTable strings;
	private final ClassTable table;
	private List<Field> fields = new ArrayList<Field>();
	private HashMap<String, Method> methods = new HashMap<String, Method>();

	int name;
	int id;
	int parent;
	int size;

	public Class(ClassTable table, String name, StringTable strings) {
		this.table = table;
		this.strings = strings;
		this.name = strings.get(name);
		this.id = table.get(this);
	}

	void setParent(int id) {
		this.parent = id;
	}

	void setParent(Class parent) {
		this.parent = this.table.get(parent);
	}

	String getName() {
		return this.strings.get(this.name);
	}

	int getId() {
		return this.id;
	}

	@Override
	public int hashCode() {
		return this.getName().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null)
			return false;
		if (obj instanceof Class) {
			Class c = (Class) obj;
			return this.getName().equals(c.getName());
		}

		return false;
	}

	public void addField(Field field) {
		int size = this.fields.size();
		this.fields.add(field);
		Integer typeSize = getTypeSize(field.type.getName());
		this.size += typeSize;

		if (size > 0) {
			Field prev = fields.get(size - 1);
			int offset = prev.offset;
			typeSize = null;
			typeSize = getTypeSize(prev.type.getName());

			offset += typeSize;

			field.offset = offset;
		}

	}

	public Field getField(String name) {
		for (Field field : this.fields)
			if (field.getName().equals(name))
				return field;
		return null;
	}

	private int getTypeSize(String name) {
		Integer size = primitiveSizes.get(name);
		if (size == null)
			return 8;
		return size;
	}

	@Override
	public String toString() {
		return "Class[" + this.getId() + ":" + this.getName() + "]";
	}

	public void addMethod(Method method) {
		methods.put(method.getName(), method);
	}

	public Method getMethod(String name) {
		return this.methods.get(name);
	}
}
